﻿using Jacobi.Vst.Core;
using Jacobi.Vst.Framework;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;

namespace Aleator
{
    #pragma warning disable 1591

    /// <summary>
    /// Implements the incoming Midi event handling for the plugin.
    /// </summary>
    public class MidiProcessor : IVstMidiProcessor
    {
        private Plugin _plugin;

        //May need this for VST compliance
        //private double m_tempo;

        /// <summary>
        /// Current tempo of the host
        /// </summary>
        public double Tempo
        {
            get
            {
                //TODO: Notifying UI of tempo changes from host. 1 is the minimum allowed so just return that if there's no sequencer
                //detected.
                if ((_plugin.PluginAudioProcessor as AudioProcessor) == null 
                    || (_plugin.PluginAudioProcessor as AudioProcessor).TimeInfo == null)
                    return 120.00;
                else
                    return (_plugin.PluginAudioProcessor as AudioProcessor).TimeInfo.Tempo;
            }
        }

        /// <summary>
        /// Constructs a new instance.
        /// </summary>
        /// <param name="plugin">Must not be null.</param>
        public MidiProcessor(Plugin plugin)
        {
            _plugin = plugin;
            Events = new VstEventCollection();
            NoteOnEvents = new Queue<byte>();
        }

        /// <summary>
        /// Gets the midi events that should be processed in the current cycle.
        /// </summary>
        public VstEventCollection Events { get; private set; }

        /// <summary>
        /// The raw note on note numbers.
        /// </summary>
        public Queue<byte> NoteOnEvents { get; private set; }

        #region IVstMidiProcessor Members

        public int ChannelCount
        {
            get { return _plugin.ChannelCount; }
        }

        public void Process(VstEventCollection events)
        {
            foreach (VstEvent evnt in events)
            {
                if (evnt.EventType != VstEventTypes.MidiEvent) continue;

                VstMidiEvent midiEvent = (VstMidiEvent)evnt;
                VstMidiEvent mappedEvent = null;

                if ( ((midiEvent.Data[0] & 0xF0) == 0x80 || (midiEvent.Data[0] & 0xF0) == 0x90) || (midiEvent.Data[0] & 0xF0) == 0xb0)
                {
                    byte[] midiData = new byte[4];
                    midiData[0] = midiEvent.Data[0];
                    midiData[1] = midiEvent.Data[1];
                    midiData[2] = midiEvent.Data[2];

                    mappedEvent = new VstMidiEvent(midiEvent.DeltaFrames, 
                        midiEvent.NoteLength, 
                        midiEvent.NoteOffset, 
                        midiData, 
                        midiEvent.Detune, 
                        midiEvent.NoteOffVelocity);

                    Events.Add(mappedEvent);

                    // add raw note-on note numbers to the queue
                    if((midiEvent.Data[0] & 0xF0) == 0x90)
                    {
                        lock (((ICollection)NoteOnEvents).SyncRoot)
                        {
                            NoteOnEvents.Enqueue(midiEvent.Data[1]);
                        }
                    }
                }
            }

            try
            {
                _plugin.PluginAudioProcessor.Process(null, null);
            }
            catch (Exception)
            {
                //Do nothing
            }

        }

        #endregion
    }

    #pragma warning restore 1591
}
